#include "seg.h"
plugin *plugins = NULL;

int plugin_init(const char *file, int priority){
    int sockets[2];
    plugin *temp, *prev, *newplugin;

    if (socketpair(AF_UNIX,SOCK_STREAM,0,sockets)){
        error("socketpair");
        return ERR_SYS;
    }

    if (NULL == (newplugin = malloc(sizeof(plugin)))) return ERR_MEM;
    newplugin->state = STATE_LOADING;
    newplugin->fd = sockets[0];
    newplugin->file = NULL;
    newplugin->name = NULL;
    newplugin->desc = NULL;
    newplugin->pid = -1;
    newplugin->ver = 0;
    newplugin->mortal = 1;
    newplugin->priority = priority;
    newplugin->commands = NULL;
    temp = find_smaller_fd(newplugin->fd);
    newplugin->next = temp;
    if (NULL == (prev=get_prev_plugin(temp))) plugins = newplugin;
    else prev->next = newplugin;

    switch (newplugin->pid = fork()){
    case -1:
        error("fork");
        return ERR_SYS;
        break;
    case 0:
        close(sockets[0]);
        if (dup2(sockets[1],STDIN_FILENO) == -1 || dup2(sockets[1],STDOUT_FILENO) == -1 || dup2(sockets[1],STDERR_FILENO) == -1) exit(0);
        execl(file,file,(char*)NULL);
        _exit(errno);
    default:
        close(sockets[1]);
        if (NULL == (newplugin->file = malloc(MAX_FILE_LEN)) ||
            NULL == (newplugin->name = malloc(MAX_ATTR_LEN)) ||
            NULL == (newplugin->desc = malloc(MAX_ATTR_LEN))){
            murder(newplugin->pid);
            return ERR_MEM;
        }
        strncpy(newplugin->file,file,MAX_FILE_LEN);
        strncpy(newplugin->name,"null",strlen("null"));
        strncpy(newplugin->desc,"null",strlen("null"));
        plugin_watch(newplugin);
        newplugin->state = STATE_RUNNING;
        return OK;
    }
}

int plugin_kill(plugin *dead){
    plugin *temp_plugin;

    close(dead->fd);
    dead->state = STATE_DEAD;
    plugin_unwatch(dead);
    murder(dead->pid);
    if (NULL == (temp_plugin = get_prev_plugin(dead))) plugins = dead->next;
    else temp_plugin->next = dead->next;

    free(dead->file);
    free(dead->name);
    free(dead->desc);
    free(dead);

    return OK;
}

void cmd_plugin_init(plugin *p, const char **argv){
    tell(p,"%d\n",plugin_init(argv[1],p->priority));
}

void cmd_plugin_kill(plugin *p, const char **argv){
    plugin *temp_plugin;
    if (NULL == (temp_plugin = file_to_plugin(argv[1]))){
        tell(p,"err invalid plugin file\n");
        return;
    }
    if (!temp_plugin->mortal){
	    tell(p,"err immortal\n");
	    return;
    }
    tell(p,"%d\n",plugin_kill(temp_plugin));
}

void cmd_plugin_list(plugin *p){
    plugin *temp;
    tell(p,"plugins: ");
    for(temp = plugins; temp; temp = temp->next){
        tell(p,"%s ",temp->file);
    }
    tell(p,"\n");
}

int plugin_setinfo(plugin *p, const char *name, const char *desc, const int ver){
    p->name = strndup(name,MAX_ATTR_LEN);
    p->desc = strndup(desc,MAX_ATTR_LEN);
    p->ver = ver;
    return OK;
}

void cmd_plugin_setinfo(plugin *p, const char **argv){
    tell(p,"%d\n",plugin_setinfo(p, argv[1], argv[2], atoi(argv[3])));
}

void cmd_plugin_getinfo(plugin *p, const char **argv){
    plugin *temp_plugin = file_to_plugin(argv[1]);
    if (NULL == temp_plugin && NULL == (temp_plugin = name_to_plugin(argv[1]))){
        tell(p,"invalid plugin\n");
        return;
    }
    tell(p,"info: %s %d\n",temp_plugin->name?temp_plugin->name:"null",temp_plugin->ver);
}

void cmd_plugin_state(plugin *p, const char **argv){
    plugin *temp;
    temp = file_to_plugin(argv[1]);
    if (temp == NULL){
        tell(p,"invalid plugin file\n");
        return;
    }
    tell(p,"state: %d\n",temp->state);
}

int plugins_init(void){
    term = malloc(sizeof(plugin));

    if ((term->fd = socket(AF_UNIX,SOCK_STREAM,0))==-1){
        error("socket");
        quit();
    }

    dup2(STDIN_FILENO,term->fd);
    dup2(STDOUT_FILENO,term->fd);
    dup2(STDERR_FILENO,term->fd);

    term->file = strdup("term");
    term->name = strdup("term");
    term->desc = strdup("the i/o for the terminal");
    term->ver = 0;
    term->pid = 0;
    term->priority = 100;
    term->mortal = 0;
    term->next = NULL;
    term->state = STATE_RUNNING;
    plugins = term;

    if (command_add(term,"plugin_init",&cmd_plugin_init,1,1) ||
        command_add(term,"plugin_kill",&cmd_plugin_kill,1,1) ||
        command_add(term,"plugin_list",&cmd_plugin_list,0,0) ||
        command_add(term,"plugin_state",&cmd_plugin_state,1,1) ||
        command_add(term,"plugin_setinfo",&cmd_plugin_setinfo,3,3) ||
        command_add(term,"plugin_getinfo",&cmd_plugin_getinfo,1,1))
        return ERR_SYS;
    return 0;
}

char *plugin_to_str(plugin *p){
    return p->name;
}

plugin *name_to_plugin(const char *name){
    plugin *temp;
    for(temp=plugins;temp;temp=temp->next){
        if (0==strcmp(temp->name,name)) return temp;
    }
    return NULL;
}

plugin *pid_to_plugin(pid_t pid){
    plugin *temp;
    for(temp=plugins;temp;temp=temp->next){
        if (pid == temp->pid || temp->pid == -1) return temp;
    }
    return NULL;
}

plugin *file_to_plugin(const char *file){
    plugin *temp;
    for(temp=plugins;temp;temp=temp->next){
        if (0==strcmp(temp->file,file)) return temp;
    }
    return NULL;
}

plugin *fd_to_plugin(int fd){
    plugin *temp;
    for(temp=plugins;temp;temp=temp->next){
        if (temp->fd == fd) return temp;
    }
    return NULL;
}

plugin *find_smaller_fd(int fd){
    plugin *temp;
    for(temp=plugins;temp;temp=temp->next){
        if (temp->fd < fd) return temp;
    }
    return NULL;
}

plugin *get_prev_plugin(plugin *p){
    plugin *temp_plugin;
    if (plugins == p) return NULL;
    for(temp_plugin = plugins;temp_plugin;temp_plugin=temp_plugin->next){
        if (temp_plugin && temp_plugin->next == p) return p;
    }
    return NULL;
}

int murder(pid_t pid){
    if (pid == 0 || pid == -1) return OK;
    kill(pid,SIGHUP);
    return OK;
}

void tell(plugin *p, const char *fmt, ...){
    va_list args;
    char data[256];

    va_start(args, fmt);
    vsnprintf(data, 256, fmt, args);
    va_end(args);

    if (write(p->fd, data, strlen(data)) == -1) {
        error("write");
    }
}

void tellall(const char *fmt, ...){
    va_list args;
    char data[256];
    plugin *temp;

    va_start(args,fmt);
    vsnprintf(data,256,fmt,args);
    va_end(args);

    for(temp=plugins;temp;temp=temp->next)
        tell(temp,"%s",data);
}
